package com.learn.redis.lock.annotation;

import com.learn.core.utils.AopUtils;
import com.learn.redis.lock.CusLockInfo;
import com.learn.redis.lock.LockService;
import com.learn.redis.lock.LockServiceFactory;
import com.learn.redis.utils.idempotent.Idempotent;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author PC
 * 幂等性界面处理类
 */
@Aspect
@Component
public class LockAspect {

    private final static Logger LOGGER = LoggerFactory.getLogger(LockAspect.class);

    private LockServiceFactory lockServiceFactory;

    private Idempotent idempotent;

    private AopUtils aopUtils;

    @Autowired
    public void setLockServiceFactory(LockServiceFactory lockServiceFactory) {
        this.lockServiceFactory = lockServiceFactory;
    }

    @Autowired
    public void setIdempotent(Idempotent idempotent) {
        this.idempotent = idempotent;
    }

    @Autowired
    public void setAopUtils(AopUtils aopUtils) {
        this.aopUtils = aopUtils;
    }

    @Around("@annotation(lock)")
    public Object around(ProceedingJoinPoint joinPoint, Lock lock) throws Throwable {
        String lockKey = aopUtils.getElValue(joinPoint, lock.lockKey());
        if (StringUtils.isEmpty(lockKey)) {
            LOGGER.error("lockKey is empty");
            return null;
        }
        //校验lockKey是否是被伪造的，lockKey的生成和校验逻辑可以自定义
        if (lock.lockKeyExistFlag() && !idempotent.lockKeyExist(lockKey)) {
            LOGGER.error("the lockKey was forged");
            return null;
        }
        LockService lockService = lockServiceFactory.getLockServiceByType(lock.lockType());
        CusLockInfo cusLockInfo = new CusLockInfo();
        cusLockInfo.setLockKey(lockKey);
        cusLockInfo.setWaitTime(lock.waitTime());
        cusLockInfo.setLeaseTime(lock.leaseTime());
        cusLockInfo.setTimeUnit(lock.timeUnit());
        try {
            lockService.lock(cusLockInfo);
            if (!lockService.lock(cusLockInfo)) {
                throw new RuntimeException("Failed to acquire lock");
            }
            return joinPoint.proceed();
        } finally {
            //开启了伪造校验，结束后需进行清除
            if (lock.lockKeyExistFlag() && !idempotent.clear(lockKey)) {
                LOGGER.error("Verification resource lockKey clearance failed, lockKey value is{}", lockKey);
            }
            lockService.unLock(cusLockInfo);
        }
    }
}
